home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Pascal / Applications / NIH Image 1.60 / 1.60 Source / User.p < prev    next >
Encoding:
Text File  |  1996-03-01  |  10.7 KB  |  424 lines  |  [TEXT/PJMM]

  1. unit User;
  2.  
  3. {This module is a good place to put user additions to NIH Image. You will need }
  4. {to uncomment the call to InitUser in Image.p.}
  5.  
  6.  
  7. interface
  8.  
  9.     uses
  10.         Types, Memory, QuickDraw, QuickDrawText, Packages, Menus, Events, Fonts, 
  11.         Scrap, ToolUtils, Resources, Errors, Palettes, StandardFile, Windows,
  12.         Controls, TextEdit, Files, Dialogs, TextUtils, Finder, MixedMode, Processes,
  13.         globals, Utilities, Graphics, Filters, Analysis;
  14.  
  15.  
  16.     procedure InitUser;
  17.     procedure DoUserCommand1;
  18.     procedure DoUserCommand2;
  19.     procedure DoUserMenuEvent (MenuItem: integer);
  20.     procedure OldUserMacroCode (CodeNumber: integer; Param1, Param2, Param3: extended);
  21.     procedure UserMacroCode (str: str255; Param1, Param2, Param3: extended);
  22.  
  23.  
  24. implementation
  25.  
  26. {User global variables go here.}
  27.     var
  28.         color, MinSpacing: integer;
  29.         SaveInfo: InfoPtr;
  30.         PeakRadius, Peakedness: extended;
  31.  
  32.  
  33.     procedure InitUser;
  34.     begin
  35.         UserMenuH := GetMenu(UserMenu);
  36.         InsertMenu(UserMenuH, 0);
  37.         DrawMenuBar;
  38. {Additional user initialization code goes here.}
  39.     end;
  40.  
  41.  
  42.     procedure DrawDot (row, column, RowOffset, ColumnOffset: integer; big: boolean);
  43.         var
  44.             h, v: integer;
  45.     begin
  46.         if big then begin
  47.                 for h := -1 to 1 do
  48.                     for v := -1 to 1 do
  49.                         PutPixel(column * 16 + ColumnOffset * 4 + h + 16, row * 16 + RowOffset * 4 + v + 16, color)
  50.             end
  51.         else
  52.             PutPixel(column * 16 + ColumnOffset * 4 + 16, row * 16 + RowOffset * 4 + 16, color);
  53.     end;
  54.  
  55.     procedure DrawNeighborhood (i, row, column: integer);
  56.  
  57.     begin
  58.         DrawDot(row, column, 0, 0, BitAnd(i, 1) = 1);
  59.         DrawDot(row, column, 0, 1, BitAnd(i, 2) = 2);
  60.         DrawDot(row, column, 0, 2, BitAnd(i, 4) = 4);
  61.         DrawDot(row, column, 1, 2, BitAnd(i, 8) = 8);
  62.         DrawDot(row, column, 2, 2, BitAnd(i, 16) = 16);
  63.         DrawDot(row, column, 2, 1, BitAnd(i, 32) = 32);
  64.         DrawDot(row, column, 2, 0, BitAnd(i, 64) = 64);
  65.         DrawDot(row, column, 1, 0, BitAnd(i, 128) = 128);
  66.         DrawDot(row, column, 1, 1, true);
  67.     end;
  68.  
  69.  
  70.     procedure SetColor (i: integer);
  71. {Color neighborhoods to show which ones would be removed on the first pass(150), second pass(100),}
  72. {or either pass(200) when using the Zhang and Suen thinning algorithm(CACM, Mar. 1984,236-239).}
  73.         var
  74.             p2, p3, p4, p5, p6, p7, p8, p9, A, B: integer;
  75.     begin
  76.         p2 := bsr(band(i, 2), 1);
  77.         p3 := bsr(band(i, 4), 2);
  78.         p4 := bsr(band(i, 8), 3);
  79.         p5 := bsr(band(i, 16), 4);
  80.         p6 := bsr(band(i, 32), 5);
  81.         p7 := bsr(band(i, 64), 6);
  82.         p8 := bsr(band(i, 128), 7);
  83.         p9 := band(i, 1);
  84.         A := 0;
  85.         if (p2 = 0) and (p3 = 1) then
  86.             A := A + 1;
  87.         if (p3 = 0) and (p4 = 1) then
  88.             A := A + 1;
  89.         if (p4 = 0) and (p5 = 1) then
  90.             A := A + 1;
  91.         if (p5 = 0) and (p6 = 1) then
  92.             A := A + 1;
  93.         if (p6 = 0) and (p7 = 1) then
  94.             A := A + 1;
  95.         if (p7 = 0) and (p8 = 1) then
  96.             A := A + 1;
  97.         if (p8 = 0) and (p9 = 1) then
  98.             A := A + 1;
  99.         if (p9 = 0) and (p2 = 1) then
  100.             A := A + 1;
  101.         B := p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
  102.         color := 255;
  103.         if A = 1 then
  104.             if (B >= 2) and (B <= 6) then begin
  105.                     if ((p2 * p4 * p6 = 0) and (p4 * p6 * p8 = 0)) and ((p2 * p4 * p8 = 0) and (p2 * p6 * p8 = 0)) then
  106.                         color := 200
  107.                     else if (p2 * p4 * p6 = 0) and (p4 * p6 * p8 = 0) then
  108.                         color := 150
  109.                     else if (p2 * p4 * p8 = 0) and (p2 * p6 * p8 = 0) then
  110.                         color := 100;
  111.                 end;
  112.     end;
  113.  
  114.  
  115.     procedure DoUserCommand1;
  116. {Generates a table showing all possible 3x3 neighborhoods. This table is used}
  117. { for making up the "fate table" used by the Skeletonize command and the Wand tool.}
  118.         var
  119.             row, column, index: integer;
  120.     begin
  121.         row := 0;
  122.         column := 0;
  123.         if NewPicWindow('Fate Table', 600, 200) then
  124.             for index := 0 to 255 do begin
  125.                     SetColor(index);
  126.                     DrawNeighborhood(index, row, column);
  127.                     column := column + 1;
  128.                     if column = 32 then begin
  129.                             row := row + 1;
  130.                             column := 0;
  131.                         end;
  132.                 end;
  133.     end;
  134.  
  135.  
  136.     function isPeak (x, y, minValue: LongInt): boolean;
  137.         var
  138.             delta, angle, dx, dy: extended;
  139.             v, i, v2, maxv2, x2, y2, v2count, nSamples: integer;
  140.             sample: LineType;
  141.             minlower, count, nLower, maxCount: integer;
  142.             PeakFound: boolean;
  143.             mask: rect;
  144.     begin
  145.         isPeak := false;
  146.         v := MyGetPixel(x, y);
  147.         if v < minValue then
  148.             exit(isPeak);
  149.         if v <= MyGetPixel(x + 1, y) then
  150.             exit(isPeak);
  151.         if v <= MyGetPixel(x + 1, y + 1) then
  152.             exit(isPeak);
  153.         if v <= MyGetPixel(x, y + 1) then
  154.             exit(isPeak);
  155.         if v <= MyGetPixel(x - 1, y + 1) then
  156.             exit(isPeak);
  157.         if v < MyGetPixel(x - 1, y) then
  158.             exit(isPeak);
  159.         if (v < MyGetPixel(x - 1, y - 1)) then
  160.             exit(isPeak);
  161.         if v < MyGetPixel(x, y - 1) then
  162.             exit(isPeak);
  163.         if v < MyGetPixel(x + 1, y - 1) then
  164.             exit(isPeak);
  165.         nSamples := round(4 * PeakRadius);
  166.         delta := 2.0 * pi / nsamples;
  167.         angle := 0.0;
  168.         maxv2 := round((1.0 - Peakedness) * v);
  169.         for i := 1 to nSamples do begin
  170.                 dx := PeakRadius * cos(angle);
  171.                 dy := PeakRadius * sin(angle);
  172.                 sample[i] := round(GetInterpolatedPixel(x + dx, y + dy));
  173.                 angle := angle + delta;
  174.             end;
  175.         minLower := round(0.677 * nsamples);
  176.         PeakFound := false;
  177.         count := 0;
  178.         i := 1;
  179.         nLower := 0;
  180.         maxCount := nSamples + minLower;
  181.         repeat
  182.             if sample[i] <= maxv2 then
  183.                 nLower := nLower + 1
  184.             else
  185.                 nLower := 0;
  186.             PeakFound := nLower >= minLower;
  187.             i := i + 1;
  188.             if i > nSamples then
  189.                 i := 1;
  190.             count := count + 1;
  191.         until PeakFound or (count = maxCount);
  192.         if PeakFound then begin
  193.                 info := SaveInfo;
  194.                 with info^ do begin
  195.                         SetRect(RoiRect, x - MinSpacing + 1, y - MinSpacing + 1, x + MinSpacing, y + MinSpacing);
  196.                         with RoiRect do begin
  197.                                 if left < 0 then
  198.                                     left := 0;
  199.                                 if top < 0 then
  200.                                     top := 0;
  201.                                 if right > PicRect.right then
  202.                                     right := PicRect.right;
  203.                                 if bottom > PicRect.bottom then
  204.                                     bottom := PicRect.bottom;
  205.                             end;
  206.                         GetRectHistogram;
  207.                         PeakFound := histogram[0] = 0;
  208.                     end; {with}
  209.                 Info := UndoInfo;
  210.             end;
  211.         isPeak := PeakFound;
  212.     end;
  213.  
  214.  
  215.     procedure FindPeaks (minValue, PeakRadiusP, PeakednessP: extended);
  216.         var
  217.             x, y, i, iMinValue: integer;
  218.             AutoSelectAll: boolean;
  219.             srect, mask: rect;
  220.             count: LongInt;
  221.             t: FateTable;
  222.     begin
  223.         if NotRectangular or NotInBounds or NoUndo then
  224.             exit(FindPeaks);
  225.         iMinValue := round(minValue);
  226.         if iMinValue < 10 then
  227.             iMinValue := 10;
  228.         if iMinValue > 150 then
  229.             iMinValue := 150;
  230.         PeakRadius := PeakRadiusP;
  231.         if PeakRadius = 0.0 then
  232.             PeakRadius := 6.0;
  233.         if PeakRadius < 1.0 then
  234.             PeakRadius := 1.0;
  235.         if PeakRadius > 50.0 then
  236.             PeakRadius := 50.0;
  237.         MinSpacing := round(PeakRadius) - 1;
  238.         if MinSpacing < 1 then
  239.             MinSpacing := 1;
  240.         if MinSpacing > 4 then
  241.             MinSpacing := 4;
  242.         Peakedness := PeakednessP;
  243.         if Peakedness = 0.0 then
  244.             Peakedness := 0.2;
  245.         if Peakedness < 0.05 then
  246.             Peakedness := 0.05;
  247.         if Peakedness > 0.95 then
  248.             Peakedness := 0.95;
  249.         AutoSelectAll := not Info^.RoiShowing;
  250.         if AutoSelectAll then
  251.             SelectAll(true);
  252.         ShowWatch;
  253.         SetupUndo;
  254.         WhatToUndo := UndoEdit;
  255.         SetupUndoInfoRec;
  256.         SaveInfo := Info;
  257.         srect := info^.roiRect;
  258.         KillRoi;
  259.         ChangeValues(0, 0, 1);
  260.         info := UndoInfo;
  261.         count := 0;
  262.         with srect do
  263.             for y := top to bottom - 1 do begin
  264.                     if CommandPeriod then begin
  265.                             beep;
  266.                             Info := SaveInfo;
  267.                             leave;
  268.                         end;
  269.                     for x := left to right - 1 do
  270.                         if isPeak(x, y, iMinValue) then begin
  271.                                 count := count + 1;
  272.                                 Info := SaveInfo;
  273.                                 PutPixel(x, y, 0);
  274. {PutPixel(x - 1, y, 0);}
  275. {PutPixel(x - 1, y - 1, 0);}
  276. {PutPixel(x, y - 1, 0);}
  277.                                 SetRect(mask, x - 1, y - 1, x + 1, y + 1);
  278.                                 UpdateScreen(mask);
  279.                                 Info := UndoInfo;
  280.                                 if count < MaxMeasurements then begin
  281.                                         User1^[count] := x;
  282.                                         User2^[count] := y;
  283.                                     end;
  284.                                 if (y mod 50) = 0 then ShowMessage(concat(long2str(y), '  ', long2str(count)));
  285.                             end;
  286.                 end;
  287.         Info := SaveInfo;
  288.         if count < MaxMeasurements then begin
  289.                 UnsavedResults := false;
  290.                 ResetCounter;
  291.                 for i := 1 to count do begin
  292.                         ClearResults(i);
  293.                         xcenter^[i] := User1^[i];
  294.                         ycenter^[i] := User2^[i];
  295.                     end;
  296.                 mCount := count;
  297.                 UpdateList;
  298.                 ShowInfo;
  299.             end
  300.         else
  301.             PutError('"Max Measurements" is too small.');
  302.         ShowMessage(concat('Count=', long2str(count), crStr, 'Threshold=', long2str(iMinValue)));
  303.     end;
  304.  
  305.  
  306.  
  307.     procedure ComputeBirefringence (scale, offset: extended);
  308. {This an example of how to do image math using a UserCode macro routine.}
  309. {It executes the following formula}
  310.  
  311.       {SQRT ( ( I1 - I2 ) ^ 2 + ( I3 - I4 ) ^ 2 ) / ( I1 + I2 - I3 + I4 ) ,}
  312.  
  313. {where I1 , I2 , I3 , I4  are the first four slices of the current stack.}
  314. {The result in the fifth slice of the stack.}
  315.  
  316.         var
  317.             i1, i2, i3, i4, i5: LineType;
  318.             i, slice, row: integer;
  319.             mask: rect;
  320.             v, min, max: extended;
  321.             minstr, maxstr: str255;
  322.     begin
  323.         with info^ do begin
  324.                 if StackInfo = nil then
  325.                     exit(ComputeBirefringence);
  326.                 if StackInfo^.nSlices <> 5 then
  327.                     exit(ComputeBirefringence);
  328.                 min := 1.0e12;
  329.                 max := -1.0e12;
  330.                 for row := 0 to nLines - 1 do begin
  331.                         SelectSlice(1);
  332.                         GetLine(0, row, PixelsPerLine, i1);
  333.                         SelectSlice(2);
  334.                         GetLine(0, row, PixelsPerLine, i2);
  335.                         SelectSlice(3);
  336.                         GetLine(0, row, PixelsPerLine, i3);
  337.                         SelectSlice(4);
  338.                         GetLine(0, row, PixelsPerLine, i4);
  339.                         for i := 0 to PixelsPerLine - 1 do begin
  340.                                 v := sqrt(sqr(I1[i] - I2[i]) + sqr(I3[i] - I4[i])) / (I1[i] + I2[i] - I3[i] + I4[i]);
  341.                                 if v < min then
  342.                                     min := v;
  343.                                 if v > max then
  344.                                     max := v;
  345.                                 if v > 255 then
  346.                                     v := 255;
  347.                                 if v < 0 then
  348.                                     v := 0;
  349.                                 v := v * scale + offset;
  350.                                 i5[i] := round(v);
  351.                             end;
  352.                         SelectSlice(5);
  353.                         PutLine(0, row, PixelsPerLine, i5);
  354.                         SetRect(mask, 0, row, PixelsPerLine, row + 1);
  355.                         UpdateScreen(mask);
  356.                         if CommandPeriod then
  357.                             leave;
  358.                     end;
  359.             end;
  360.         RealToString(min, 1, 4, minstr);
  361.         RealToString(max, 1, 4, maxstr);
  362.         ShowMessage(concat('min=', minstr, crStr, 'max=', maxstr));
  363.     end;
  364.  
  365.  
  366.     procedure ShowNoCodeMessage;
  367.     begin
  368.         PutError('Requires user written Pascal routine. ');
  369.     end;
  370.  
  371.  
  372.     procedure DoUserCommand2;
  373.     begin
  374.         ShowNoCodeMessage
  375.     end;
  376.  
  377.  
  378.     procedure DoUserMenuEvent (MenuItem: integer);
  379.     begin
  380.         case MenuItem of
  381.             1: 
  382.                 DoUserCommand1;
  383.             2: 
  384.                 DoUserCommand2;
  385.         end;
  386.     end;
  387.  
  388.  
  389.     procedure OldUserMacroCode (CodeNumber: integer; Param1, Param2, Param3: extended);
  390.   {Obsolete version kept for backward compatibilty.}
  391.     begin
  392.         case CodeNumber of
  393.             1: 
  394.                 ShowNoCodeMessage;
  395.             2: 
  396.                 ShowNoCodeMessage;
  397.             3: 
  398.                 ShowNoCodeMessage;
  399.             4: 
  400.                 ShowNoCodeMessage;
  401.             5: 
  402.                 FindPeaks(param1, param2, param3);
  403.             otherwise
  404.                 ShowNoCodeMessage;
  405.         end;
  406.     end;
  407.  
  408.  
  409.     procedure UserMacroCode (str: str255; Param1, Param2, Param3: extended);
  410.     begin
  411.         MakeLowerCase(str);
  412.         if pos('peaks', str) <> 0 then begin
  413.                 FindPeaks(param1, param2, param3);
  414.                 exit(UserMacroCode);
  415.             end;
  416.         if pos('birefringence', str) <> 0 then begin
  417.                 ComputeBirefringence(param1, param2);
  418.                 exit(UserMacroCode);
  419.             end;
  420.         ShowNoCodeMessage;
  421.     end;
  422.  
  423.  
  424. end.